package com.yeyks.rocketmq;

import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import cn.hutool.json.XML;
import com.alibaba.fastjson.JSON;
import com.aliyun.openservices.ons.api.Action;
import com.aliyun.openservices.ons.api.ConsumeContext;
import com.aliyun.openservices.ons.api.Message;
import com.aliyun.openservices.ons.api.MessageListener;
import com.google.common.collect.Lists;
import com.yeyks.cnbooking.Result.callback.*;
import com.yeyks.common.base.Arguments;
import com.yeyks.common.constants.cnbooking.EsCnPrice;
import com.yeyks.common.utils.DateUtils;
import com.yeyks.common.utils.apputil.redis.RedisService;
import com.yeyks.common.utils.apputil.rediskey.BuilderKey;
import com.yeyks.common.utils.apputil.rediskey.RedisKey;
import com.yeyks.search.constants.EsIndexConstant;
import com.yeyks.search.pojo.model.EsHotelInfo;
import com.yeyks.search.pojo.model.EsRoomCalendarInfo;
import com.yeyks.search.pojo.model.EsRoomInfo;
import com.yeyks.search.pojo.model.base.EsDocType;
import com.yeyks.search.service.ElasticSearchService;
import com.yeyks.search.service.hotel.EsImportHotelService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.util.Collections;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * CnBooking 推送修改延迟队列Listener
 *
 * @author Administrator
 */

@Slf4j
@Component
public class MessageCnBookingPushListener implements MessageListener {

    private final ElasticSearchService elasticSearchService;

    private final EsImportHotelService esImportHotelService;

    private final RedisService redisService;

    @Autowired
    public MessageCnBookingPushListener(ElasticSearchService elasticSearchService, EsImportHotelService esImportHotelService, RedisService redisService) {
        this.elasticSearchService = elasticSearchService;
        this.esImportHotelService = esImportHotelService;
        this.redisService = redisService;
    }

    /**
     * 处理监听到的房态数据更新，更具类型同步更新es中酒店房态数据
     *
     * @param msg 消息信息
     */
    private void disposeLogic(String msg) {
        try {
            JSONObject jsonObject = XML.toJSONObject( msg );
            jsonObject = (JSONObject) jsonObject.get( "DataSync" );
            CbDataSync cbDataSync = JSONUtil.toBean( jsonObject, CbDataSync.class );
            if ( null != cbDataSync ) {
                CbData data = cbDataSync.getData( );
                //两种选择一种（第一种全量更新，耗时，第二种方便，但是数据更新会出问题）
                this.editCalendarStatusInES(data.getHotel() , data.getRoom(), data.getRatePlans());
                //this.editOnlyCalendarInES( data.getHotel(), data.getRoom(), data.getRatePlans() );
            }
            assert cbDataSync != null;
        } catch (Exception e) {
            //e.printStackTrace( );
            log.error( "MessageCnBookingPush：处理监听消息异常：【转换消息异常】", e);
        }
    }

    /**
     * 更新ES中CnBooking的【房态】数据
     * （直接进行import） --》 房间Id --》 房态Id
     *
     * @param hotelData 酒店
     */
    private void editCalendarStatusInES(CbHotelData hotelData, CbRoomData roomData, CbRatePlansData cbRatePlansData) {
        //验证酒店地址
        String hotelId = EsIndexConstant.ID_PREX_CNBOOKING_HOTEL + hotelData.getHotelId( );
        List<EsHotelInfo> hotelInfoList = elasticSearchService.esHotelSearch( EsIndexConstant.INDEX_HOTEL2,
                "cn", hotelId, 10, 1 );
        if ( !CollectionUtils.isEmpty( hotelInfoList ) ) {
            EsHotelInfo esHotelInfo = hotelInfoList.get( 0 );
            //查询比例
            if ( Arguments.strNotNull( esHotelInfo.getProvinceId( ) ) && Arguments.strNotNull( esHotelInfo.getCityId( ) ) ) {

                //清除酒店,房态
                elasticSearchService.deleteCalendarByHotelAndRoomId( hotelData.getHotelId( ), roomData.getRoomId(),
                        EsIndexConstant.INDEX_HOTEL2 );
                //调用单个CN酒店导入覆盖，全量更新（房间ID）
                //esImportHotelService.importRoomAndCalendarByHotel( esHotelInfo, roomData.getRoomId(), EsIndexConstant.INDEX_HOTEL2 );
                //直接获取-->更新房间和房态
                editRoomAndCalendar(esHotelInfo, roomData.getRoomId(), cbRatePlansData, EsIndexConstant.INDEX_HOTEL2);
            }
        }
    }

    /**
     * 更新ES的房间和房态
     * @param roomId 房间ID
     * @param ratePlansData 房态结果信息
     */
    private void editRoomAndCalendar(EsHotelInfo esHotelInfo, String roomId, CbRatePlansData ratePlansData, String indexName){
        List<EsRoomCalendarInfo> roomCalendarRoomList = Lists.newArrayList( );
        EsRoomCalendarInfo esRoomCalendarInfo = null;
        EsRoomInfo esRoomInfo = null;
        //1.得到房态的价格比例
        double highPercent = EsCnPrice.HIGH_PERCENT;
        double lowPercent = EsCnPrice.LOW_PERCENT;
        String redisKey = BuilderKey.build( RedisKey.CITY_PERCENT_PRICE,
                esHotelInfo.getProvinceId() + ":" + esHotelInfo.getCityId() );
        String redisValue = redisService.get( redisKey );
        if ( Arguments.strNotNull( redisValue ) ) {
            String[] split = redisValue.split( "-" );
            lowPercent = Double.parseDouble( split[ 0 ] );
            highPercent = Double.parseDouble( split[ 1 ] );
        }
        //2.组装获取当前房间信息
        List<EsRoomInfo> esRoomInfos = elasticSearchService.esRoomSearch( indexName,
                "cn",
                EsIndexConstant.ID_PREX_CNBOOKING_ROOM + roomId,
                10, 1 );
        if(!CollectionUtils.isEmpty( esRoomInfos )){
            esRoomInfo = esRoomInfos.get(0);
            //3.确定录入 价格计划ID
            String ratePlanId = "";
            List<CbRateData> realRateData = null;
            if (null != ratePlansData && !CollectionUtils.isEmpty(ratePlansData.getRatePlan())) {
                List<CbRatePlanData> ratePlan = ratePlansData.getRatePlan();
                for (CbRatePlanData cbRatePlanData : ratePlan) {
                    List<CbRateData> rate = cbRatePlanData.getRate( );
                    if ( !CollectionUtils.isEmpty( rate ) ) {
                        CbRateData rateData = rate.get( 0 );
                        if ( Arguments.strNotNull( rateData.getRateId() ) ) {
                            //满足条件的价格计划ID
                            ratePlanId = cbRatePlanData.getRatePlanId();
                            //满足条件的房态
                            realRateData = cbRatePlanData.getRate();
                            break;
                        }
                    }
                }
                if(!CollectionUtils.isEmpty( realRateData )){
                    //房间最新计划ID
                    esRoomInfo.setRatePlanId( ratePlanId );
                    //选取第一条数据价格 -> 房间信息完善
                    convertEsRoomNowPrice(esRoomInfo, realRateData.get(0), lowPercent, highPercent);
                    for (CbRateData cbRateData :realRateData){
                        //4. 房态集合信息组装
                        esRoomCalendarInfo = convertRoomCalendar(esHotelInfo.getSourceId(),
                                roomId, ratePlanId, cbRateData, lowPercent, highPercent);
                        if(null != esRoomCalendarInfo){
                            roomCalendarRoomList.add(esRoomCalendarInfo);
                        }
                    }
                    //ES存储更新房间和房态
                    if ( !CollectionUtils.isEmpty( roomCalendarRoomList ) ) {
                        //批量更新【房间】的数据信息
                        elasticSearchService.createMoreRoomDocument( Collections.singletonList( esRoomInfo ), indexName );
                        //批量更新【房态】的数据信息
                        elasticSearchService.createMoreRoomCalendarDocument( roomCalendarRoomList, indexName);
                        log.info("房态同步成功！当前线程【{}】, 房间ID【{}】, 酒店ID 【{}】",
                                Thread.currentThread().getName(), esRoomInfo.getSourceId(), esRoomInfo.getHotelId());
                    }
                }
            }
        }

    }

    /**
     * 房间价格更新
     * @param roomInfo 房间信息
     * @param cbRateData 数据
     * @param lowPercent 最低价
     * @param highPercent 最高价
     */
    private void convertEsRoomNowPrice(EsRoomInfo roomInfo, CbRateData cbRateData, double lowPercent, double highPercent){

        int bottomPrice = (int) (Double.parseDouble( cbRateData.getPrice( ) ) * lowPercent) * 100;
        int curPrice = (int) (Double.parseDouble( cbRateData.getPrice( ) ) * highPercent) * 100;
        roomInfo.setBottomPrice( bottomPrice );
        roomInfo.setCurPrice( curPrice );
        roomInfo.setOrigPrice( (int) (Double.parseDouble( cbRateData.getPrice( ) )) * 100);
    }

    /**
     * 房态数据转换
     * @param hotelId 酒店Id
     * @param roomId 房间Id
     * @param cbRateData 房态
     * @param lowPercent 最低
     * @param highPercent 最高
     * @return 返回对象信息
     */
    private EsRoomCalendarInfo convertRoomCalendar(
                                                   String hotelId,
                                                   String roomId,
                                                   String ratePlanId,
                                                   CbRateData cbRateData,
                                                   double lowPercent, double highPercent){
        EsRoomCalendarInfo esRoomCalendarInfo = null;
        //如果不是人民币,直接跳过
        if ( !"CNY".equals( cbRateData.getCurrency( ) ) ) {
            return null;
        }
        //过滤价格
        if ( StringUtils.isEmpty( cbRateData.getPrice( ) ) ) {
            return null;
        }
        if(StringUtils.isEmpty( cbRateData.getRateId())) {
            //这就是问题的所在，因为房态可能已经发生了变化
            return null;
        }
        int bottomPrice = (int) (Double.parseDouble( cbRateData.getPrice( ) ) * lowPercent) * 100;
        int curPrice = (int) (Double.parseDouble( cbRateData.getPrice( ) ) * highPercent) * 100;

        esRoomCalendarInfo = new EsRoomCalendarInfo( );
        //id =（前缀 + rateId + 时间）
        String id = EsIndexConstant.ID_PREX_CNBOOKING_CALENDAR + cbRateData.getRateId()
                + DateUtils.toYYMMddStr( DateUtils.strXmlToDate( cbRateData.getDate( ) ) );

        esRoomCalendarInfo.setCalendarId( id );
        EsDocType docType = new EsDocType( );
        docType.setParent( EsIndexConstant.ID_PREX_CNBOOKING_ROOM + roomId);
        docType.setName( EsIndexConstant.JOIN_TYPE_CALENDAR );
        esRoomCalendarInfo.setDocType( docType );
        esRoomCalendarInfo.setDate( DateUtils.strXmlToDate( cbRateData.getDate( ) ) );
        esRoomCalendarInfo.setSourceType( "cn" );
        //时间字段必须更新进去
        esRoomCalendarInfo.setDate( DateUtils.strXmlToDate( cbRateData.getDate( ) ) );
        esRoomCalendarInfo.setSourceId( String.valueOf( cbRateData.getRateId()) );
        esRoomCalendarInfo.setRoomId( Integer.valueOf( roomId ) );
        esRoomCalendarInfo.setHotelId( Integer.valueOf( hotelId ) );
        esRoomCalendarInfo.setCount( Integer.valueOf( cbRateData.getCount( ) ) );

        //计划ID必须有
        esRoomCalendarInfo.setRatePlanId(ratePlanId);

        esRoomCalendarInfo.setId( Long.parseLong( cbRateData.getRateId() ) );
        //0:不可用  1：可用
        int status;
        if ( "026001".equals( cbRateData.getStatu( ) ) ) {
            status = 1;
            esRoomCalendarInfo.setBottomPrice( bottomPrice );
            esRoomCalendarInfo.setSalePrice( curPrice );
            esRoomCalendarInfo.setOriginPrice( (int) (Double.parseDouble( cbRateData.getPrice( ) ) * 100) );
        } else {
            status = 0;
            int price = Double.valueOf( cbRateData.getPrice( ) ).intValue( );
            if ( price >= 99999 ) {
                redisService.setAndExpire( RedisKey.CNBOOKING_MAX_PRICE + ":" + id,
                        JSON.toJSONString( cbRateData ),
                        10,
                        TimeUnit.DAYS );
            } else {
                //如果下架的时候是正常的价格，需要记录到Redis中，方便检测问题，保存记录10天
                esRoomCalendarInfo.setBottomPrice( bottomPrice );
                esRoomCalendarInfo.setSalePrice( curPrice );
                esRoomCalendarInfo.setOriginPrice( (int) (Double.parseDouble( cbRateData.getPrice( ) ) * 100) );
            }
        }
        esRoomCalendarInfo.setStatus( status );
        return esRoomCalendarInfo;
    }

    /**
     * 仅修改日历信息
     *
     * @param hotelData     酒店
     * @param roomData      房间
     * @param ratePlansData 计划
     */
    private void editOnlyCalendarInES(CbHotelData hotelData, CbRoomData roomData, CbRatePlansData ratePlansData) {
        double highPercent = EsCnPrice.HIGH_PERCENT;
        double lowPercent = EsCnPrice.LOW_PERCENT;
        List<EsRoomCalendarInfo> roomCalendarRoomList = Lists.newArrayList( );
        EsRoomCalendarInfo esRoomCalendarInfo = null;
        //验证酒店地址
        String hotelId = EsIndexConstant.ID_PREX_CNBOOKING_HOTEL + hotelData.getHotelId( );
        List<EsHotelInfo> hotelInfoList = elasticSearchService.esHotelSearch( EsIndexConstant.INDEX_HOTEL2,
                "cn", hotelId, 10, 1 );
        if ( !CollectionUtils.isEmpty( hotelInfoList ) ) {
            EsHotelInfo esHotelInfo = hotelInfoList.get( 0 );
            //查询比例
            if ( Arguments.strNotNull( esHotelInfo.getProvinceId( ) ) && Arguments.strNotNull( esHotelInfo.getCityId( ) ) ) {
                String redisKey = BuilderKey.build( RedisKey.CITY_PERCENT_PRICE, esHotelInfo.getProvinceId( ) + ":" + esHotelInfo.getCityId( ) );
                String redisValue = redisService.get( redisKey );
                if ( Arguments.strNotNull( redisValue ) ) {
                    String[] split = redisValue.split( "-" );
                    lowPercent = Double.parseDouble( split[ 0 ] );
                    highPercent = Double.parseDouble( split[ 1 ] );
                }
            }
        }

        //循环更新房态信息
        if ( null != ratePlansData && !CollectionUtils.isEmpty( ratePlansData.getRatePlan( ) ) ) {
            for (CbRatePlanData cbRatePlanData : ratePlansData.getRatePlan( )) {
                if ( !CollectionUtils.isEmpty( cbRatePlanData.getRate( ) ) ) {
                    String ratePlanId = cbRatePlanData.getRatePlanId( );
                    String cnRoomId = EsIndexConstant.ID_PREX_CNBOOKING_ROOM + roomData.getRoomId( );
                    List<EsRoomInfo> roomInfoList = elasticSearchService.esRoomSearch( EsIndexConstant.INDEX_HOTEL2,
                            "cn", cnRoomId, 10, 1 );
                    //如果没有这个房间，直接过滤
                    if ( CollectionUtils.isEmpty( roomInfoList ) ) {
                        continue;
                    }
                    //如果有房间，但是价格计划ID不同，也需要过滤
                    if ( !ratePlanId.equals( roomInfoList.get( 0 ).getRatePlanId( ) ) ) {
                        continue;
                    }
                    for (CbRateData cbRateData : cbRatePlanData.getRate( )) {
                        //如果不是人民币,直接跳过
                        if ( !"CNY".equals( cbRateData.getCurrency( ) ) ) {
                            continue;
                        }
                        //过滤价格
                        if ( StringUtils.isEmpty( cbRateData.getPrice( ) ) ) {
                            continue;
                        }
                        if(null == cbRatePlanData.getRate().get(0)){
                            continue;
                        }
                        if(StringUtils.isEmpty( cbRatePlanData.getRate().get(0).getRateId())) {
                            //这就是问题的所在，因为房态可能已经发生了变化
                            continue;
                        }
                        int bottomPrice = (int) (Double.parseDouble( cbRateData.getPrice( ) ) * lowPercent) * 100;
                        int curPrice = (int) (Double.parseDouble( cbRateData.getPrice( ) ) * highPercent) * 100;

                        esRoomCalendarInfo = new EsRoomCalendarInfo( );
                        //id =（前缀 + rateId + 时间）
                        String id = EsIndexConstant.ID_PREX_CNBOOKING_CALENDAR + cbRatePlanData.getRate( ).get( 0 ).getRateId( )
                                + DateUtils.toYYMMddStr( DateUtils.strXmlToDate( cbRateData.getDate( ) ) );

                        esRoomCalendarInfo.setCalendarId( id );
                        EsDocType docType = new EsDocType( );
                        docType.setParent( EsIndexConstant.ID_PREX_CNBOOKING_ROOM + roomData.getRoomId( ) );
                        docType.setName( EsIndexConstant.JOIN_TYPE_CALENDAR );
                        esRoomCalendarInfo.setDocType( docType );
                        esRoomCalendarInfo.setDate( DateUtils.strXmlToDate( cbRateData.getDate( ) ) );
                        esRoomCalendarInfo.setSourceType( "cn" );
                        //时间字段必须更新进去
                        esRoomCalendarInfo.setDate( DateUtils.strXmlToDate( cbRateData.getDate( ) ) );
                        esRoomCalendarInfo.setSourceId( String.valueOf( cbRatePlanData.getRate( ).get( 0 ).getRateId( ) ) );
                        esRoomCalendarInfo.setRoomId( Integer.valueOf( roomData.getRoomId( ) ) );
                        esRoomCalendarInfo.setHotelId( Integer.valueOf( hotelData.getHotelId( ) ) );
                        esRoomCalendarInfo.setCount( Integer.valueOf( cbRateData.getCount( ) ) );

                        //计划ID必须有
                        esRoomCalendarInfo.setRatePlanId( cbRatePlanData.getRatePlanId( ) );

                        esRoomCalendarInfo.setId( Long.parseLong( cbRatePlanData.getRate( ).get( 0 ).getRateId( ) ) );
                        //0:不可用  1：可用
                        int status;
                        if ( !Arguments.strIsNull( cbRatePlanData.getRatePlanStatus( ) ) ) {
                            // 015004 上线 ,  015005 下线
                            if ( "015004".equals( cbRatePlanData.getRatePlanStatus( ) ) ) {
                                if ( "026001".equals( cbRateData.getStatu( ) ) ) {
                                    status = 1;
                                } else {
                                    status = 0;
                                }
                            } else {
                                status = 0;
                            }
                        } else {
                            if ( "026001".equals( cbRateData.getStatu( ) ) ) {
                                status = 1;
                            } else {
                                status = 0;
                            }
                        }
                        //如果状态不等于0，在更新价格[注意，只有开启房态是才录入价格]
                        if ( status != 0 ) {
                            esRoomCalendarInfo.setBottomPrice( bottomPrice );
                            esRoomCalendarInfo.setSalePrice( curPrice );
                            esRoomCalendarInfo.setOriginPrice( (int) (Double.parseDouble( cbRateData.getPrice( ) ) * 100) );
                        } else {
                            int price = Double.valueOf( cbRateData.getPrice( ) ).intValue( );
                            if ( price >= 99999 ) {
                                redisService.setAndExpire( RedisKey.CNBOOKING_MAX_PRICE + ":" + id,
                                        JSON.toJSONString( cbRateData ),
                                        10,
                                        TimeUnit.DAYS );
                            } else {
                                //如果下架的时候是正常的价格，需要记录到Redis中，方便检测问题，保存记录10天
                                esRoomCalendarInfo.setBottomPrice( bottomPrice );
                                esRoomCalendarInfo.setSalePrice( curPrice );
                                esRoomCalendarInfo.setOriginPrice( (int) (Double.parseDouble( cbRateData.getPrice( ) ) * 100) );
                            }
                        }
                        esRoomCalendarInfo.setStatus( status );
                        roomCalendarRoomList.add( esRoomCalendarInfo );
                    }
                }
            }
        }
        if ( !CollectionUtils.isEmpty( roomCalendarRoomList ) ) {
            //ES的更新（批量更新房态的数据信息）
            elasticSearchService.createMoreRoomCalendarDocument( roomCalendarRoomList, EsIndexConstant.INDEX_HOTEL2 );
        }
    }

    @Override
    public Action consume(Message message, ConsumeContext context) {
        try {
            //Long startTime = System.currentTimeMillis();
            byte[] body = message.getBody( );
            //获取到接收的消息，由于接收到的是byte数组，所以需要转换成字符串
            String msg = new String( body );
            //处理业务逻辑
            this.disposeLogic( msg );
            //Long endTime = System.currentTimeMillis();
        } catch (Exception e) {
            log.error( "MessageListener.consume error:" + e.getMessage( ), e );
        }
        return Action.CommitMessage;
    }
}
